﻿using System;
using System.Text;
using RunWrapper = Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.IO;
using System.Threading;
using System.Globalization;

// *******************************************************
// STOP!STOP!STOP!STOP!STOP!STOP!STOP!STOP!STOP!STOP!STOP!
//
// Don't look at this code, it was written for a dare.
//
// STOP!STOP!STOP!STOP!STOP!STOP!STOP!STOP!STOP!STOP!STOP!
// *******************************************************

namespace DatabaseGroup.IntegrationTests.SSIS.SourceAdapters
{
    public static class TestDataGenerator
    {
        private static FileStream _resultStream = null;

        public static void CreateFile(string file, Encoding encoding, int rows, RunWrapper.DataType[] columns,
            int headers, string textQualifier, string columnDelimiter, string rowDelimiter, bool allowBad, int maxString, 
            int culture, bool includeResultFile)
        {
            FileStream fileStream = File.Create(file, 4096);

            if (includeResultFile)
                _resultStream = File.Create(Path.Combine(Path.GetDirectoryName(file), "Results_" + Path.GetFileName(file)));

            Random r = new Random();

            byte[] preamble = encoding.GetPreamble();
            foreach (byte bom in preamble)
            {
                fileStream.WriteByte(bom);
                if (_resultStream != null)
                    _resultStream.WriteByte(bom);
            }

            for (int headerIndex = 0; headerIndex < headers; headerIndex++)
            {
                byte[] data = encoding.GetBytes(CreateHeaderRow(encoding, r) + rowDelimiter);
                fileStream.Write(data, 0, data.Length);
                if (_resultStream != null)
                    _resultStream.Write(data, 0, data.Length);
            }

            string headerNames = "";
            for (int nameIndex = 0; nameIndex < columns.Length; nameIndex++)
            {
                if (!string.IsNullOrEmpty(textQualifier))
                    headerNames += textQualifier;

                headerNames += "HEADER" + (nameIndex + 1);

                if (!string.IsNullOrEmpty(textQualifier))
                    headerNames += textQualifier;

                if ((nameIndex + 1) < columns.Length)
                    headerNames += columnDelimiter;
            }

            byte[] namedata = encoding.GetBytes(headerNames + rowDelimiter);
            fileStream.Write(namedata, 0, namedata.Length);
            if (_resultStream != null)
                _resultStream.Write(namedata, 0, namedata.Length);

            for (int rowIndex = 0; rowIndex < rows; rowIndex++)
            {
                string[] row = CreateDataRow(r, columns, textQualifier, columnDelimiter, allowBad, rowDelimiter, maxString, encoding, culture, includeResultFile);
                byte[] data = encoding.GetBytes(row[0] + rowDelimiter);
                fileStream.Write(data, 0, data.Length);

                if (_resultStream != null)
                {
                    data = encoding.GetBytes(row[1] + rowDelimiter);
                    _resultStream.Write(data, 0, data.Length);
                }
            }

            fileStream.Close();
            if (_resultStream != null)
                _resultStream.Close();
        }

        private static string[] CreateDataRow(Random r, RunWrapper.DataType[] columns, string textQualifier, string columnDelimiter,
            bool allowBad, string rowDelimiter, int maxString, Encoding e, int culture, bool includeResultFile)
        {
            StringBuilder builder = new StringBuilder();
            StringBuilder builder2 = new StringBuilder();
            bool isTextQualified = !string.IsNullOrEmpty(textQualifier);

            for (int dtIndex = 0; dtIndex < columns.Length; dtIndex++)
            {
                if (isTextQualified)
                {
                    builder.Append(textQualifier);
                    if (includeResultFile)
                        builder2.Append(textQualifier);
                }

                string col = "";
                switch (columns[dtIndex])
                {
                    case RunWrapper.DataType.DT_TEXT:
                        col = AddStringColumn(r, isTextQualified, allowBad, rowDelimiter, maxString*10, e);
                        builder.Append(col);
                        if (includeResultFile)
                            builder2.Append(col);
                        break;
                    case RunWrapper.DataType.DT_STR:
                        col = AddStringColumn(r, isTextQualified, allowBad, rowDelimiter, maxString, e);
                        builder.Append(col);
                        if (includeResultFile)
                            builder2.Append(col);
                        break;
                    case RunWrapper.DataType.DT_NUMERIC:
                        col = AddDecimalColumn(r, allowBad);
                        builder.Append(col);
                        if (includeResultFile)
                            builder2.Append(col);
                        break;
                    case RunWrapper.DataType.DT_UI1:
                        col = AddTinyIntColumn(r, allowBad);
                        builder.Append(col);
                        if (includeResultFile)
                            builder2.Append(col);
                        break;
                    case RunWrapper.DataType.DT_I4:
                        col = AddIntColumn(r, allowBad);
                        builder.Append(col);
                        if (includeResultFile)
                            builder2.Append(col);
                        break;
                    case RunWrapper.DataType.DT_DBTIMESTAMP:
                        AddDateColumn(r, allowBad, culture, builder, builder2);
                        break;
                }

                if (isTextQualified)
                {
                    builder.Append(textQualifier);
                    if (includeResultFile)
                        builder2.Append(textQualifier);
                }

                if (dtIndex + 1 < columns.Length)
                {
                    builder.Append(columnDelimiter);
                    if (includeResultFile)
                        builder2.Append(columnDelimiter);
                }
            }

            if (includeResultFile)
                return new string[] { builder.ToString(), builder2.ToString() };
            else
                return new string[] { builder.ToString() };
        }

        private static string AddStringColumn(Random r, bool isTextQualified, bool allowBad, string rowDelimiter, int maxString, Encoding e)
        {
            string col = "";
            maxString -= 10;
            col += RandomCharData(r, r.Next(maxString/2), e);

            if (isTextQualified && r.Next(100) > 50)
                col += rowDelimiter;

            col += RandomCharData(r, r.Next(maxString/2), e);

            if (allowBad && r.Next(100) > 85)
                col += RandomCharData(r, r.Next(maxString), e);

            return col;
        }

        private static string AddDecimalColumn(Random r, bool allowBad)
        {
            string col = "";
            if (allowBad && r.Next(100) > 85)
            {
                if (r.Next(100) > 70)
                    col += "DUD";
                else
                    col += RandomNumericData(r, 20);
            }

            col += RandomNumericData(r, r.Next(3, 6));
            col += ".";
            col += RandomNumericData(r, 6);

            return col;
        }

        private static string AddTinyIntColumn(Random r, bool allowBad)
        {
            string col = "";
            if (allowBad && r.Next(100) > 85)
                col += r.Next(0, 100000).ToString();

            col += r.Next(0, 256).ToString();

            if (allowBad && r.Next(100) > 95)
                col += "DUD";

            return col;
        }

        private static string AddIntColumn(Random r, bool allowBad)
        {
            string col = "";
            if (allowBad && r.Next(100) > 85)
                col += r.Next(0, int.MaxValue).ToString();

            if (r.Next(100) > 75)
                col += "-";

            col += r.Next(0, int.MaxValue).ToString();

            if (allowBad && r.Next(100) > 95)
                col += "DUD";

            return col;
        }

        private static void AddDateColumn(Random r, bool allowBad, int culture, StringBuilder b1, StringBuilder b2)
        {
            if (allowBad && r.Next(100) > 95)
            {
                b1.Append("DUD");
                if (b2 != null)
                    b2.Append("DUD");
            }

            var d = new DateTime(1900, 1, 1);
            d = d.AddDays(r.Next(1, 73000));
            d = d.AddSeconds(r.Next(1, 1000000));
            DateTimeFormatInfo provider = CultureInfo.GetCultureInfo(culture).DateTimeFormat;
            b1.Append(d.ToString("F", provider));

            if (b2 != null)
            {
                string dstr = d.ToString("u", provider);
                dstr = dstr.TrimEnd(new[] { 'Z' });
                b2.Append(dstr);
            }

            if (allowBad && r.Next(100) > 95)
            {
                b1.Append("DUD");
                if (b2 != null)
                    b2.Append("DUD");
            }
        }

        private static string CreateHeaderRow(Encoding e, Random r)
        {
            return RandomCharData(r, r.Next(100, 1000), e);
        }

        private static string RandomCharData(Random random, int length, Encoding e)
        {
            string res = "";
            string text = " abcdefghijklmnopqrstuvxyz0123456789";

            if (e != Encoding.ASCII)
                text += "абвгдеёжзийклмнопрстуфхцчшщъыьэюя电开东车红无鸟热时时佛惠拜黑冰兔妒";

            int l = text.Length;

            for (int charIndex = 0; charIndex < length; charIndex++)
            {
                res += text[random.Next(1, l)];
            }

            return res;
        }

        private static string RandomNumericData(Random random, int length)
        {
            string res = "";
            string text = " 0123456789";
            for (int charIndex = 0; charIndex < length; charIndex++)
            {
                if (charIndex + 1 == length || charIndex == 0)
                    res += text[random.Next(2, text.Length)];
                else
                    res += text[random.Next(1, text.Length)];
            }

            return res;
        }
    }
}
